/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

namespace OptixDotNet 
{
	ref class BufferStream;
	ref class Acceleration;

	/// <summary>
	/// Type of Bounding Volume Hierarchy algorithm that will be used for <see cref="OptixDotNet::Acceleration">Acceleration</see> construction.
	/// </summary>
	public enum class AccelBuilder
	{
		/// <summary>
		/// No Acceleration structure. Only suitable for very small scenes.<br/>
		/// Traversers: <see cref="OptixDotNet::AccelTraverser">NoAccel</see>.
		/// </summary>
		NoAccel,

		/// <summary>
		/// Very fast GPU based accelertion good for animated scenes.<br/>
		/// Traversers: <see cref="OptixDotNet::AccelTraverser">Bvh</see> or <see cref="OptixDotNet::AccelTraverser">BvhCompact</see>.
		/// </summary>
		Lbvh,

		/// <summary>
		/// Classic Bounding Volume Hierarchy that favors quality over construction performance.<br/>
		/// A good compromise between Sbvh and MedianBvh.<br/>
		/// Traversers: <see cref="OptixDotNet::AccelTraverser">Bvh</see> or <see cref="OptixDotNet::AccelTraverser">BvhCompact</see>.
		/// </summary>
		Bvh,

		/// <summary>
		/// Uses a fast construction scheme to produce a medium quality bounding volume hierarchy.<br/>
		/// Traversers: <see cref="OptixDotNet::AccelTraverser::Bvh">Bvh</see> or <see cref="OptixDotNet::AccelTraverser::Bvh">BvhCompact</see>.
		/// </summary>
		MedianBvh,

		/// <summary>
		/// Split-BVH, a high quality bounding volume hierarchy that is usually the best choice for static geometry.<br/>
		/// Has the highest memory footprint and construction time.<br/>
		/// Requires certain properties to be set, such as: <see cref="OptixDotNet::Acceleration::VertexBufferName">VertexBufferName</see>
		/// and <see cref="OptixDotNet::Acceleration::IndexBufferName">IndexBufferName</see>.<br/>
		/// Traversers: <see cref="OptixDotNet::AccelTraverser">Bvh</see> or <see cref="OptixDotNet::AccelTraverser">BvhCompact</see>.
		/// </summary>
		Sbvh,

		/// <summary>
		/// Constructs a high quality kd-tree and is comparable to Sbvh in traversal perfromance.<br/>
		/// Has the highest memory footprint and construction time.<br/>
		/// Requires certain properties to be set, such as: <see cref="OptixDotNet::Acceleration::VertexBufferName">VertexBufferName</see>
		/// and <see cref="OptixDotNet::Acceleration::IndexBufferName">IndexBufferName</see>.<br/>
		/// Traversers: <see cref="OptixDotNet::AccelTraverser">Bvh</see> or <see cref="OptixDotNet::AccelTraverser">BvhCompact</see>.
		/// </summary>
		TriangleKdTree
	};

	/// <summary>
	/// Type of Bounding Volume Hierarchy algorithm that will be used for <see cref="OptixDotNet::Acceleration">Acceleration</see> traversal.
	/// </summary>
	public enum class AccelTraverser
	{
		/// <summary>
		/// No Acceleration structure. Linearly traverses primitives in the scene.<br/>
		/// Builders: <see cref="AccelBuilder">NoAccel</see>.
		/// </summary>
		NoAccel,

		/// <summary>
		/// Uses a classic Bounding Volume Hierarchy traversal.<br/>
		/// Builders: <see cref="OptixDotNet::AccelBuilder">Lbvh</see>, <see cref="OptixDotNet::AccelBuilder">Bvh</see>
		/// <see cref="OptixDotNet::AccelBuilder">MedianBvh</see>, or <see cref="OptixDotNet::AccelBuilder">Sbvh</see>.<br/>
		/// </summary>
		Bvh,

		/// <summary>
		/// Compresses bvh data by a factor of 4 before uploading to the GPU. Acceleration data is decompressed on the fly during traversal.<br/>
		/// Useful for large static scenes that require more than a gigabyte of memory, and to minimize page misses for virtual memory.<br/>
		/// Builders: <see cref="OptixDotNet::AccelBuilder">Lbvh</see>, <see cref="OptixDotNet::AccelBuilder">Bvh</see>
		/// <see cref="OptixDotNet::AccelBuilder">MedianBvh</see>, or <see cref="OptixDotNet::AccelBuilder">Sbvh</see>.<br/>
		/// </summary>
		BvhCompact,

		/// <summary>
		/// Uses a kd-tree traverser.<br/>
		/// Builders: <see cref="OptixDotNet::AccelBuilder">TriangleKdTree</see>.
		/// </summary>
		KdTree
	};

	/// <summary>
	/// A Bounding Volume Hierarchy structure used to speed up traversal and intersection queries for ray-tracing.
	/// </summary>
	public ref class Acceleration : public OptixNode
	{
	public:

		/// <summary>
		/// Creates an Acceleration structure.
		/// </summary>
		/// <param name="context">Context used to create the Acceleration structure</param>
		/// <param name="builder">Type of <see cref="OptixDotNet::AccelBuilder">AccelBuilder</see> used to construct the Acceleration structure.</param>
		/// <param name="traverser">Type of <see cref="OptixDotNet::AccelTraverser">AccelTraverser</see> used to traverse the Acceleration structure.</param>
		Acceleration( Context^ context, AccelBuilder builder, AccelTraverser traverser );

		/// <summary>
		/// Checks to make sure the object is valid and consistent
        /// </summary>
		virtual void Validate() override
		{
			CheckError( rtAccelerationValidate( mAccel ) );
		}

		/// <summary>
		/// Destroys the internal Optix resource
        /// </summary>
		virtual void Destroy() override
		{
			if( mAccel )
				CheckError( rtAccelerationDestroy( mAccel ) );

			mAccel = 0;
		}

		/// <summary>
		/// Will cache the internal acceleration data to path. 
        /// </summary>
		/// <param name="path">Path of the file that the acceleration data will be written to. If 'path' does not exist it will be created.</param>
		/// <returns>True if succesful, False otherwise.</returns>
		bool Save( String^ path );

		/// <summary>
		/// If path exists, will attempt to load a previously saved acceleration cache file
        /// </summary>
		/// <param name="path">Path of the file that contains the previously cached acceleration data.</param>
		/// <returns>True if succesful, False otherwise.</returns>
		bool Load( String^ path );

		/// <summary>
		/// Invalidates the acceleration structure and notifies Optix that it needs to be rebuilt
        /// </summary>
		void MarkAsDirty()
		{
			CheckError( rtAccelerationMarkDirty( mAccel ) );
		}

		/// <summary>
		/// Queries the acceleration structure to determine if it has been marked as dirty
        /// </summary>
		bool IsDirty()
		{
			int dirty;
			CheckError( rtAccelerationIsDirty( mAccel, &dirty ) );

			return dirty == 1;
		}

		/// <summary>
		/// Sets the 'refit' property on the acceleration structure. Refit tells Optix that only small geometry changes have been made,
		/// and to NOT perform a full rebuild of the hierarchy. Only a valid property on Bvh built acceleration structures.
        /// </summary>
		property bool Refit
		{
			bool get()
			{
				if( mBuilder == AccelBuilder::Bvh )
				{
					const char* str = 0;
					CheckError( rtAccelerationGetProperty( mAccel, "refit", &str ) );

					return System::Int32::Parse( AnsiiToStr( str ) ) == 1;
				}

				return false;
			}

			void set( bool refit )
			{
				CheckError( rtAccelerationSetProperty( mAccel, "refit", ( refit ? "1" : "0" ) ) );
			}
		}

		/// <summary>
		/// Sets 'vertex_buffer_name' property of the acceleration structure. This notifies Sbvh and TriangkeKdTree builders to look at the
		/// vertex buffer assigned to 'vertex_buffer_name' in order to build the hierarchy.
		/// This must match the name of the variable the buffer is attached to.
		/// Property only valid for Sbvh or TriangleKdTree built acceleration structures.
        /// </summary>
		property System::String^ VertexBufferName
		{
			System::String^ get()
			{
				const char* str = 0;
				CheckError( rtAccelerationGetProperty( mAccel, "vertex_buffer_name", &str ) );

				return AnsiiToStr( str );
			}
			void set( System::String^ name )
			{
				CheckError( rtAccelerationSetProperty( mAccel, "vertex_buffer_name", StrToAnsii( name ) ) );
			}
		}

		/// <summary>
		/// Sets 'vertex_buffer_stride' property in bytes of the acceleration structure. This defines the offset between two vertices. Default is assumed to be 0.
		/// Property only valid for Sbvh or TriangleKdTree built acceleration structures.
        /// </summary>
		property int VertexBufferStride
		{
			int get()
			{
				if( mBuilder == AccelBuilder::Sbvh || mBuilder == AccelBuilder::TriangleKdTree )
				{
					const char* str = 0;
					CheckError( rtAccelerationGetProperty( mAccel, "vertex_buffer_stride", &str ) );

					return System::Int32::Parse( AnsiiToStr( str ) );
				}
				return 0;
			}
			void set( int stride )
			{
				CheckError( rtAccelerationSetProperty( mAccel, "vertex_buffer_stride", StrToAnsii( stride.ToString() ) ) );
			}
		}

		/// <summary>
		/// Sets 'index_buffer_name' property of the acceleration structure. This notifies Sbvh and TriangkeKdTree builders to look at the
		/// index buffer assigned to 'index_buffer_name' in order to build the hierarchy.
		/// This must match the name of the variable the buffer is attached to.
		/// Property only valid for Sbvh or TriangleKdTree built acceleration structures.
        /// </summary>
		property System::String^ IndexBufferName
		{
			System::String^ get()
			{
				const char* str = 0;
				CheckError( rtAccelerationGetProperty( mAccel, "index_buffer_name", &str ) );

				return AnsiiToStr( str );
			}
			void set( System::String^ name )
			{
				CheckError( rtAccelerationSetProperty( mAccel, "index_buffer_name", StrToAnsii( name ) ) );
			}
		}

		/// <summary>
		/// Sets 'index_buffer_stride' property in bytes of the acceleration structure. This defines the offset between two indices. Default is assumed to be 0.
		/// Property only valid for Sbvh or TriangleKdTree built acceleration structures.
        /// </summary>
		property int IndexBufferStride
		{
			int get()
			{
				if( mBuilder == AccelBuilder::Sbvh || mBuilder == AccelBuilder::TriangleKdTree )
				{
					const char* str = 0;
					CheckError( rtAccelerationGetProperty( mAccel, "index_buffer_stride", &str ) );

					return System::Int32::Parse( AnsiiToStr( str ) );
				}
				return 0;
			}
			void set( int stride )
			{
				CheckError( rtAccelerationSetProperty( mAccel, "index_buffer_stride", StrToAnsii( stride.ToString() ) ) );
			}
		}

		/// <summary>
		/// Sets 'leaf_size' property of the acceleration structure.
        /// </summary>
		property int LeafSize
		{
			int get()
			{
				const char* str = 0;
				CheckError( rtAccelerationGetProperty( mAccel, "leaf_size", &str ) );

				return System::Int32::Parse( AnsiiToStr( str ) );
			}
			void set( int size )
			{
				CheckError( rtAccelerationSetProperty( mAccel, "leaf_size", StrToAnsii( size.ToString() ) ) );
			}
		}

		/// <summary>
		/// Sets or gets the Acceleration hierarchy builder type
        /// </summary>
		property AccelBuilder Builder
		{
			AccelBuilder get()
			{
				return mBuilder;
			}
			void set( AccelBuilder builder )
			{
				CheckError( rtAccelerationSetBuilder( mAccel, StrToAnsii( builder.ToString() ) ) );
				mBuilder = builder;
			}
		}

		/// <summary>
		/// Sets or gets the Acceleration hierarchy traverser type
        /// </summary>
		property AccelTraverser Traverser
		{
			AccelTraverser get()
			{
				return mTraverser;
			}
			void set( AccelTraverser traverser )
			{
				CheckError( rtAccelerationSetTraverser( mAccel, StrToAnsii( traverser.ToString() ) ) );
				mTraverser = traverser;
			}
		}

		property BufferStream^ Data
		{
			BufferStream^ get();
			void set( BufferStream^ );
		}

	internal:
		Acceleration( Context^ context, RTacceleration accel );
		inline RTacceleration InternalPtr() { return mAccel; }

	private:
		RTacceleration mAccel;

		AccelBuilder mBuilder;
		AccelTraverser mTraverser;
	};
}